package com.sfzlot.dev.netty.server.server.decode;

import java.net.InetSocketAddress;

import com.sfzlot.dev.netty.bean.Message;
import com.sfzlot.dev.netty.bean.command.PowerSwitchCommand;
import com.sfzlot.dev.netty.constant.Constant;
import com.sfzlot.dev.netty.server.server.handler.ServerHandler;
import com.sfzlot.dev.netty.utils.MessageUtils;
import com.sfzlot.dev.netty.utils.ByteUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.util.AttributeKey;

public class Decoder extends LengthFieldBasedFrameDecoder {  
    
	final static Logger logger = LoggerFactory.getLogger(Decoder.class);
    private static final int HEAD_LENGTH = 31;  
  
    /** 
     *  
     * @param maxFrameLength 解码时，处理每个帧数据的最大长度 
     * @param lengthFieldOffset 该帧数据中，存放该帧数据的长度的数据的起始位置 
     * @param lengthFieldLength 记录该帧数据长度的字段本身的长度 
     * @param lengthAdjustment 修改帧数据长度字段中定义的值，可以为负数 
     * @param initialBytesToStrip 解析的时候需要跳过的字节数 
     * @param failFast 为true，当frame长度超过maxFrameLength时立即报TooLongFrameException异常，为false，读取完整个帧再报异常 
     */  
    public Decoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength,  
            int lengthAdjustment, int initialBytesToStrip, boolean failFast) {  
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength,  
                lengthAdjustment, initialBytesToStrip, failFast);  
    }  
      
    @Override  
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {  
    	InetSocketAddress ipSocket = (InetSocketAddress)ctx.channel().remoteAddress();
        String clientIp = ipSocket.getAddress().getHostAddress();
    	super.decode(ctx, in);
    	if (in == null) {  
            return null;  
        } 
    	String msg = ByteBufUtil.hexDump(in);
        int length = in.readableBytes();
        if (length < HEAD_LENGTH) {  
        	String vin = ctx.channel().attr(AttributeKey.<String>valueOf(Constant.VIN_KEY)).get();
        	logger.error("可读信息段比头部信息都小，你在逗我？,客户端ip地址:{}，设备编号：{}，报文：{}", clientIp, vin , msg);  
        	return null;
        } 
        
        ByteBuf inCopy = in.copy();
        if(!check(inCopy)) {
            logger.info("客户端ip地址:{}，接收非完整报文信息为:{}", clientIp, msg);  
            return null;
        }
        logger.info("客户端ip地址:{}，接收报文信息为:{}", clientIp, msg);
        
        Message message = MessageUtils.getMessage(in);
        logger.info("接收报文内容为:{}", message.toString());  
		if (!message.isCheckPass()) {
			logger.info(" +++++ 校验码不正确");
			ctx.close();
			return null;
		}
		
        // 校验设备编号是否合法
        if (message.getCommandIdentifier() == PowerSwitchCommand.DEVICE_LOGIN) {
			logger.info("-----------设备登入");
			// TODO 设备编号是否存在
			ctx.channel().attr(AttributeKey.<String>valueOf(Constant.VIN_KEY)).set(message.getVin());


			ServerHandler.connectMap.put(Constant.CHANNEL + message.getVin(), ctx.channel());
		}else {
//			if (ServerHandler.connectMap.get(Constant.CHANNEL + message.getVin()) == null) {
//				logger.info(" +++++ 非法连接");
//				ctx.close();
//				return null;
//			}
		}
        // TODO 用消息队列发送出去
		logger.info("接收的报文消息：{}",msg);
//        MessageSender.sendMessageQueue(msg + "_" + System.currentTimeMillis());
        return message; 
    }  
  
    /**
	 * 验证一、包头长度够不够
	 * 验证二、是不是##开头
	 * 验证三、是不是一个完整的报文
	 * @param in ByteBuf
	 * @return
	 */
	public static boolean check(ByteBuf in) {
		int length = in.readableBytes();
		if(length < HEAD_LENGTH) {//长度不度
			return false;
		}
		//移动指针跳过起始符
		byte[] start = "##".getBytes();
		
		byte[] st = new byte[2];
		st[0] = in.readByte();
		st[1] = in.readByte();
		if(start[0] != st[0] || start[1] != st[1]) {
			return false;
		}
		
        //读取报文头部分中，数据单元长度部分的内容
        byte b1 = in.getByte(in.readerIndex() + HEAD_LENGTH -2 - 2);
        byte b2 = in.getByte(in.readerIndex() + HEAD_LENGTH -2 - 1);
        //读取数据单元长度
        int bodyLength = ByteUtil.getIntFromBytes(b1,b2);
        if(length < (bodyLength +HEAD_LENGTH)) {
        	return false;
        }
		return true;
	}
}
